﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using WikiDoc.Formatter;
using WikiDoc.Lines;
using WikiDoc.Parser;

namespace WikiDoc
{
    /// <summary>
    /// 
    /// </summary>
    public class WikiTranslater
    {
        /// <summary>
        /// 初始化 WikiTranslater 类的新实例。
        /// </summary>
        /// <param name="defaultParser"></param>
        public WikiTranslater(IParser defaultParser)
        {
            if (defaultParser == null) throw new ArgumentNullException("defaultParser");

            DefaultParser = defaultParser;
        }

        /// <summary>
        /// 初始化 WikiTranslater 类的新实例。
        /// </summary>
        public WikiTranslater():this(new CodeParser())
        {
            _parsers.Add("csv", new CsvParser());
        }

        private readonly Dictionary<string, IParser> _parsers = new Dictionary<string, IParser>();

        /// <summary>
        /// Gets or sets the default parser.
        /// </summary>
        /// <value>The default parser.</value>
        [NotNull]
        public IParser DefaultParser { get; set; }

        /// <summary>
        /// Parses the specified STR.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <param name="formatter">The formatter.</param>
        /// <returns></returns>
        public virtual string Parse(string str, IFormatter formatter = null)
        {
            var reader = new StringReader(str);
            
            formatter = formatter ?? new XmlDocFormatter();
            
            Parse(reader, formatter);
            
            reader.Close();
            
            return formatter.ToString();
        }

        /// <summary>
        /// Parses the specified stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="formatter">The formatter.</param>
        public virtual void Parse(TextReader stream, IFormatter formatter)
        {
            if (stream == null) throw new ArgumentNullException("stream");
            if (formatter == null) throw new ArgumentNullException("formatter");

            var reader = new WikiReader(stream);

            reader.ReadLine();

            while (reader.CurrentLine.Type != LineTypes.Eof)
            {
                var info = reader.CurrentLine;

                switch (info.Type)
                {
                    case LineTypes.Normal:
                        _paragraphParser.Process(reader, formatter);
                        break;
                    case LineTypes.Empty:
                        reader.ReadLine();
                        break;
                    case LineTypes.BulletList:
                        _listParser.Process(reader, formatter);
                        break;
                    case LineTypes.NumberList:
                        _listParser.Process(reader, formatter);
                        break;
                    case LineTypes.AlphaList:
                        _listParser.Process(reader, formatter);
                        break;
                    case LineTypes.DefineList:
                        _defineListParser.Process(reader, formatter);
                        break;
                    case LineTypes.Indent:
                        ParseIndent(reader, formatter);
                        break;
                    case LineTypes.Comment:
                        reader.ReadLine();
                        break;
                    case LineTypes.Header:
                        var iheader = (HeaderLine)info;
                        formatter.BeginTag ("h" + iheader.Level);
                        formatter.AppendContent(iheader.Content);
                        formatter.EndTag("h" + iheader.Level);
                        reader.ReadLine();
                        break;
                    case LineTypes.HorizonLine:
                        formatter.BeginTag("hr");
                        var size = ((HorizonLine) reader.CurrentLine).Level - 3;
                        if (size > 5)
                        {
                            size = 5;
                        }
                        formatter.AddAttribute("size", size.ToString());
                        formatter.EndTag("hr");
                        reader.ReadLine();
                        break;
                    case LineTypes.CustomSection:
                        ParseCustomSection(reader, formatter);
                        break;
                    case LineTypes.CodeTag:
                        ParseCodeTag(reader, formatter);
                        break;
                    case LineTypes.Table:
                        _tableParser.Process(reader, formatter);
                        break;
                    default:
                        throw new InvalidOperationException("不可能到达这里！");
                }
            }
        }


        private readonly TableParser _tableParser = new TableParser();

        private readonly ListParser _listParser = new ListParser();

        private readonly DefineListParser _defineListParser = new DefineListParser();

        private readonly ParagraphParser _paragraphParser = new ParagraphParser();

        private static void ParseIndent(WikiReader reader, IFormatter formatter)
        {
            var sb = new StringBuilder();

            while (reader.CurrentLine.Type == LineTypes.Indent || reader.CurrentLine.Type == LineTypes.Empty)
            {
                var line = reader.CurrentLine.Line;

                if (line.Length == 0)
                {
                    sb.AppendLine();
                }
                else
                {
                    sb.Append(line, 1, line.Length -1).AppendLine();
                }
                
                reader.ReadLine();
            }

            if (sb.Length > 0)
            {
                formatter.BeginTag("code");
                formatter.AppendContent(sb.ToString());
                formatter.EndTag("code");
            }
        }

        private static void ParseCodeTag(WikiReader reader, IFormatter formatter)
        {
            var tag = ((CodeLine) reader.CurrentLine).Tag.Trim();
            var endTag = string.Concat("<", tag, "/>");
            var sb = new StringBuilder();
            string line;

            sb.AppendLine(reader.CurrentLine.Line);

            while ((line = reader.BaseReader.ReadLine()) != null)
            {
                sb.AppendLine(line);

                if (line.TrimEnd().EndsWith(endTag)) break;
            }

            if (sb.Length > 0)
            {
                formatter.Writer.WriteLine(SimpleTranslate(sb.ToString()));
            }

            reader.ReadLine();
        }

        /// <summary>
        /// 读取一个完整的代码段，退出时当前行为代码段后的第一行
        /// </summary>
        private void ParseCustomSection(WikiReader reader, IFormatter formatter)
        {
            if (reader.CurrentLine.Type != LineTypes.CustomSection)
            {
                return;
            }

            var parserName = ((CustomSectionLine) reader.CurrentLine).ParserName.ToLower();
            var argv = (reader.CurrentLine as CustomSectionLine).Argv;
            var parser = _parsers.ContainsKey(parserName) ? _parsers[parserName] : DefaultParser;
            string line;
            var sb = new StringBuilder();

            while ((line = reader.BaseReader.ReadLine()) != null)
            {
                if (line.TrimEnd().EndsWith("}}}"))
                {
                    break;
                }

                sb.AppendLine(line);
            }

            parser.Process(argv, new StringReader(sb.ToString()), formatter);
            reader.ReadLine();
        }

        /// <summary>
        /// 捕获粗体、斜体、下划线等简单wiki格式
        /// </summary>
        public static readonly Regex SimpleReplaceRegex = new Regex(
            @"(?<b>''')   (?<content> .*?) (?:''')" + // 粗体<b>
            @"|(?<u>___)  (?<content> .*?) (?:___)" + // 下划线<u>
            @"|(?<s>---)  (?<content> [^-].*?) (?:---)" + // 删除线<s>
            @"|(?<i>///)  (?<content> .*?) (?:///)" + // 斜体<i>
            @"|(?<sub>,,)   (?<content> .*?) (?:,,)" + // 下标<sub>
            @"|(?<sup>\^\^) (?<content> .*?) (?:\^\^)" + // 上标<sup>
            @"",
            RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace
            );

        private static readonly string[] SimpleTags = { "b", "u", "s", "i", "sub", "sup" };

        /// <summary>
        /// 转换粗体、斜体、下划线等格式指示
        /// </summary>
        /// <param name="wikiText">The wikiText.</param>
        /// <returns></returns>
        public static string SimpleTranslate(string wikiText)
        {
            return SimpleReplaceRegex.Replace(
                wikiText,
                match => String.Format("<{0}>{1}</{0}>",
                                       SimpleTags.First(tag => match.Groups[tag].Success),
                                       SimpleTranslate(match.Groups["content"].Value)));
        }
    }
}
